package com.malty.gateway.sign;

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.DigestUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * get 请求验签
 *
 * @author malty
 */
public class QueryParamSignStrategy extends ApiSignStrategy {
    /**
     * 计算签名，这里以一个简单的MD5算法作为示例。
     * 实际项目中应使用更安全的加密算法，如HMAC-SHA256。
     *
     * @param signKey   签名密钥
     * @param sourceStr 待签名字符串
     * @return 签名结果
     */
    @Override
    public String doCalculateSign(String signKey, String sourceStr) {
        String plaintext = String.format("%s_sign_%s", signKey, sourceStr);
        return DigestUtils.md5DigestAsHex(plaintext.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 从GET请求的URI查询参数中提取所有参数，并合并请求头中的签名参数。
     *
     * @param exchange 当前HTTP请求
     * @return 包含所有签名参与参数的不可变Map
     */
    @Override
    public Mono<Map<String, String>> getParamMap(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        // 1. 提取查询参数
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        Map<String, String> paramMap = queryParams.entrySet().stream()
                .filter(entry -> !ObjectUtils.isEmpty(entry.getKey()) &&
                        !ObjectUtils.isEmpty(entry.getValue()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().get(0),
                        (oldValue, newValue) -> oldValue,
                        HashMap::new
                ));

        // 2. 提取并合并请求头中的签名参数 (timestamp, nonce)
        Map<String, String> headerParams = extractHeaderParam(request);
        paramMap.putAll(headerParams);

        // 移除签名参数本身，防止重复签名或签名参数被篡改
        paramMap.remove("sign");

        return Mono.just(paramMap);
    }
}
